/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package main;

/**
 *
 * @author Ej-Dý
 */
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import tree.*;
import main.Token.*;

import tree.BinaryTree.Operator;
import static main.Token.*;

/**
 * Syntakticky analyzator.
 */
public class ParserAnalyzer {

    /**
     * Lexikalni analyzator.
     */
    private LexAnalyzer lexer;
    /**
     * Nacteny lexikalni symbol.
     */
    private Token token;
    private SymTab symTab = new SymTab();
    private FunTab funTab = new FunTab();
    private ArrayTab aTab = new ArrayTab();
    private int dimensionCount = 0;
    static private List<FunTeloTree> fts;

    static public List<FunTeloTree> getFTS() {
        return fts;
    }
    /**
     * Pocet semantickych chyb nalezenych v parseru.
     */
    private int errorCount;

    /**
     * Vytvori syntakticky analyzator, ktery bude dostavat
     * lexikalni symboly od zadaneho lexikalniho analyzatoru.
     */
    public ParserAnalyzer(LexAnalyzer lexer) {
        this.lexer = lexer;
        nextToken();
        symTab = new SymTab();
    }

    /**
     * Nacte dalsi lexikalni symbol.
     */
    void nextToken() {
        token = lexer.nextToken();
        if (token == COMENT) {
            nextToken();
        }
    }

    /**
     * Vypise chybu.
     */
    void error(Position p1, Position p2, String s) {
        System.out.printf("%s%s %s%n", p1, p2, s);
    }

    void error(String s) {
        Position p1 = lexer.getBeginPosition();
        Position p2 = lexer.getEndPosition();
        error(p1, p2, s);
    }

    /**
     * Semanticka chyba, napr. neznamy identifikator.
     * Nezpusobi zastaveni parseru.
     */
    void semanticError(Position p1, Position p2, String s) {
        error(p1, p2, s);
        errorCount++;
    }

    /**
     * Operace srovnani.
     */
    void accept(Token expected) {
        if (token == expected) {
            nextToken();
        } else {
            System.out.printf("%s expected: %s, found: %s%n", lexer.getBeginPosition(), expected, token);
            throw new ParserException();
        }
    }

//    program	:	START block END;
    ProgramTree program() {
        fts = new ArrayList<FunTeloTree>();
        Position p1 = lexer.getBeginPosition();
        accept(START);
        BlockTree b = block();
        accept(END);
        Position p2 = lexer.getEndPosition();
        return new ProgramTree(p1, p2, b);

    }

//block	:	deklarace funkce vlastniProgram;
//        
    BlockTree block() {
        List<VlastniProgramTree> st = new ArrayList<VlastniProgramTree>();

        Position p1 = lexer.getBeginPosition();
        symTab = deklarace();
        funTab = funkce();

        st = vlastniProgram(st, symTab);
        Position p2 = lexer.getEndPosition();
        return new BlockTree(p1, p2, symTab, funTab, st, fts);


    }

//deklarace
//	:	VAR deklaraceDal
//    |   ;
    SymTab deklarace() {

        List<VariableTree> p = new ArrayList<VariableTree>();
        if (token == VAR) {
            accept(VAR);
            deklaraceDal(symTab, p);

        }
        return symTab;


    }
    //deklaraceDal
    //:	IDENT zbytekDeklarace DVOJTECKA type STREDNIK deklarace
    //|       ;

    void deklaraceDal(SymTab s, List<VariableTree> p) {
        if (token == IDENT) {
            Position p1 = lexer.getBeginPosition();
            String n = lexer.getIdentifier();
            accept(IDENT);
            Position p2 = lexer.getLastEndPosition();
            VariableTree v = new VariableTree(p1, p2, n);
            p.add(v);
            zbytekDeklarace(p);


            accept(DVOJTECKA);
            Type t = type();
            for (VariableTree vt : p) {
                vt.setType(t);
                if (s.contains(vt.getName())) {
                    semanticError(vt.getStart(), vt.getEnd(), vt.getName() + " is already declared");
                } else {
                    s.insert(vt);
                }
            }
            accept(STREDNIK);

            deklarace();
        }

    }
//zbytekDeklarace
//	:	CARKA IDENT zbytekDeklarace
//	|	;

    void zbytekDeklarace(List<VariableTree> p) {
        if (token == CARKA) {
            accept(CARKA);
            Position p1 = lexer.getBeginPosition();
            String n = lexer.getIdentifier();
            accept(IDENT);
            Position p2 = lexer.getLastEndPosition();
            VariableTree v = new VariableTree(p1, p2, n);
            p.add(v);
            zbytekDeklarace(p);
        }

    }

    Type type() {
        switch (token) {
            case INT:
                accept(INT);
                return Type.INTEGER;
            case REAL:
                accept(REAL);
                return Type.REAL;
            case STRING:
                accept(STRING);
                return Type.STRING;
            case ARRAY:
                accept(ARRAY);
                return Type.ARRAY;
            default:
                error("expected: TYPE, found: " + token);
                throw new ParserException();
        }
    }

    //funkce	:   type funkceIdent 
    //	|	;
    FunTab funkce() {

        if (token == REAL || token == INT) {
            //FunTab f=new FunTab();
            Type t = type();
            funkceIdent(t);

        }



        return funTab;


    }
    //    funkceIdent
    //          :	IDENT LZ argument PZ funkceTelo funkce;

    void funkceIdent(Type t) {
        SymTab s = new SymTab();
        s.setFS();

        Position p1 = lexer.getBeginPosition();
        String n = lexer.getIdentifier();
        accept(IDENT);
        accept(LZ);
        ArgumentTree arg = argument(t, s);
        accept(PZ);
        Position p2 = lexer.getLastEndPosition();
        VariableFunTree vf = new VariableFunTree(p1, p2, n, null, t, arg);



        if (funTab.contains(vf.getName())) {
            semanticError(vf.getStart(), vf.getEnd(), vf.getName() + " is already declared");

        } else {

            funTab.insert(vf);
        }
        FunTeloTree ft = funTelo(s, n, arg, t);
        fts.add(ft);
        vf.setFt(ft);



        funkce();



    }

    //argument:	IDENT zbytekArgument
    //	|	;
    ArgumentTree argument(Type t, SymTab s) {
        if (token == IDENT) {

            List<VariableTree> p = new ArrayList<VariableTree>();
            Position p1 = lexer.getBeginPosition();
            String n = lexer.getIdentifier();
            accept(IDENT);
            Position p2 = lexer.getLastEndPosition();
            VariableTree v = new VariableTree(p1, p2, n);
            p.add(v);
            return zbytekArgument(1, s, p, t, p1);
        }
        return null;
    }

    //zbytekArgument
    //        :	CARKA IDENT  zbytekArgument
    //        |       ;
    ArgumentTree zbytekArgument(int pocet, SymTab s, List<VariableTree> p, Type t, Position ps) {
        if (token == CARKA) {
            pocet++;
            accept(CARKA);
            Position p1 = lexer.getBeginPosition();
            String n = lexer.getIdentifier();
            accept(IDENT);
            Position p2 = lexer.getLastEndPosition();
            VariableTree v = new VariableTree(p1, p2, n);
            p.add(v);
            return zbytekArgument(pocet, s, p, t, ps);
        }
        for (VariableTree v : p) {
            v.setType(t);
            if (s.contains(v.getName())) {
                semanticError(v.getStart(), v.getEnd(), v.getName() + " is already declared");
            } else {
                s.insert(v);
            }
        }
        Position p2 = lexer.getLastEndPosition();
        return new ArgumentTree(ps, p2, s, pocet);
    }

    //funkceTelo
    //:	LSZ vlastniProgram castReturn PSZ;
    FunTeloTree funTelo(SymTab s, String name, ArgumentTree arg, Type t) {
        List<VlastniProgramTree> st = new ArrayList<VlastniProgramTree>();
        Position p1 = lexer.getBeginPosition();
        accept(LSZ);
        vlastniProgram(st, s);
        VyrazTree v = castReturn(s);
        accept(PSZ);
        Position p2 = lexer.getLastEndPosition();
        return new FunTeloTree(p1, p2, st, v, name, arg, t);


    }

    //castReturn
    //	:	RETURN castReturnZbytek;
    VyrazTree castReturn(SymTab s) {
        accept(RETURN);
        return castReturnZbytek(s);
    }

    //castReturnZbytek
    //	:       IDENT
    //	|	CISLO
    //  |       CISLOREAL;
    VyrazTree castReturnZbytek(SymTab s) {
        switch (token) {
            case IDENT: {
                Position p1 = lexer.getBeginPosition();
                String n = lexer.getIdentifier();
                accept(IDENT);
                Position p2 = lexer.getLastEndPosition();
                VariableTree v = s.find(n);
                if (v == null) {
                    semanticError(p1, p2, "unknown identifier: " + n);
                }
                IdentifierTree i = new IdentifierTree(p1, p2, n);
                i.setVariable(v);
                return i;
            }
            case CISLO: {
                Position p1 = lexer.getBeginPosition();
                Object v = lexer.getValue();
                accept(CISLO);
                Position p2 = lexer.getLastEndPosition();
                return new LiteralTree(p1, p2, Type.INTEGER, v);
            }
            case CISLOREAL: {
                Position p1 = lexer.getBeginPosition();
                Object v = lexer.getValue();
                accept(CISLOREAL);
                Position p2 = lexer.getLastEndPosition();
                return new LiteralTree(p1, p2, Type.REAL, v);
            }
            default:
                error("expected: IDENT or CISLO or CISLOREAL, found: " + token);
                throw new ParserException();
        }
    }

//vlastniProgram
//	:	cast vlastniProgram
//	|	;
    List<VlastniProgramTree> vlastniProgram(List<VlastniProgramTree> st, SymTab s) {

        switch (token) {
            case IF:
            case FOR:
            case IDENT:
            case OPEN:
            case CLOSE:
            case WRITELN:
            case WRITE:
            case WR:
            case WRL:
                st.add(cast(s));

                vlastniProgram(st, s);

            // default:error("expected: IF or FOR of IDENT, found: " + token);
            // throw new ParserException();
        }
        return st;

    }

//cast	:       castIf
//      |	castFor
//      |	IDENT castVyraz STREDNIK
//      |       castPrirazeniDoPole
//      |	;
    VlastniProgramTree cast(SymTab s) {
        switch (token) {
            case IF: {

                return castIf(s);

            }
            case FOR: {

                return castFor(s);

            }
            case IDENT: {

                VlastniProgramTree vyraz = castVyraz(s);
                accept(STREDNIK);
                return vyraz;
            }
            
            case OPEN: {
                return open(s);
            }

            
            case CLOSE: {
                return close(s);
            }


            case WRITELN:
            case WR:
            case WRL:
            case WRITE: {

                return writeln(s);

            }
        }
        return null;
    }

    FileOpenWorkerTree open(SymTab s) {
        boolean read = false;

        Position p1 = lexer.getBeginPosition();

        accept(OPEN);
        accept(LZ);
        if((Integer)lexer.getValue()==0){
            read=true;
        }else{
            
        }
        accept(CISLO);
        accept(CARKA);
        VyrazTree nameFile = vyraz(s);

        accept(PZ);
        accept(STREDNIK);
        Position p2 = lexer.getLastEndPosition();
        return new FileOpenWorkerTree(p1, p2, nameFile, read);
    }

    FileCloseWorkerTree close(SymTab s) {
        boolean read = false;
        Position p1 = lexer.getBeginPosition();

        
            read = true;
            accept(CLOSE);
        
       

        accept(LZ);

        VyrazTree nameFile = vyraz(s);

        accept(PZ);
         accept(STREDNIK);
        Position p2 = lexer.getLastEndPosition();
        return new FileCloseWorkerTree(p1, p2, nameFile, read);
    }

//writeln	:	WRITELN LZ writeCast PZ STREDNIK;
    WriteTree writeln(SymTab s) {
        boolean line = false;
        WriteTree wrt;
        if (token == WRITELN || token == WRL) {
            line = true;
        } else {
        }
        switch (token) {
            case WRITE:
            case WRITELN: {
                if (token == WRITE) {
                    accept(WRITE);
                } else {
                    accept(WRITELN);
                }
                accept(LZ);
                wrt = writelnCast(s);
                wrt.setLine(line);
                accept(PZ);
                accept(STREDNIK);
                return wrt;

            }

            case WR:
            case WRL: {
                if (token == WR) {
                    accept(WR);
                } else {
                    accept(WRL);
                }
                accept(LZ);
                wrt = writeFileCast(s);
                wrt.setLine(line);
                accept(PZ);
                accept(STREDNIK);
                return wrt;

            }
            default:
                error("expected: WRITE or WRTITELN or WR or WRL, found: " + token);
                throw new ParserException();
        }

    }

    WriteFileTree writeFileCast(SymTab s) {
        Position p1 = lexer.getBeginPosition();
        VyrazTree where = vyraz(s);
        accept(CARKA);
        VyrazTree vt = vyraz(s);
        Position p2 = lexer.getLastEndPosition();
        return new WriteFileTree(p1, p2, where, vt);
    }

//writelnCast
//	:       IDENT
//	|	CISLO
//	|	CISLOREAL
//      |       STRING;
    WriteTree writelnCast(SymTab s) {
        Position p1 = lexer.getBeginPosition();
        /*
        switch (token) {
        case CISLO: {
        
        Object v = lexer.getValue();
        accept(CISLO);
        Position p2 = lexer.getLastEndPosition();
        LiteralTree l = new LiteralTree(p1, p2, Type.INTEGER, v);
        
        return new WriteTree(p1, p2, l);
        
        }
        case CISLOREAL: {
        
        Object v = lexer.getValue();
        accept(CISLOREAL);
        Position p2 = lexer.getLastEndPosition();
        LiteralTree l = new LiteralTree(p1, p2, Type.REAL, v);
        return new WriteTree(p1, p2, l);
        
        }
        case IDENT: {
        
        String n = lexer.getIdentifier();
        accept(IDENT);
        Position p2 = lexer.getLastEndPosition();
        VariableTree v = s.find(n);
        if (v == null) {
        semanticError(p1, p2, "unknown identifier: " + n);
        }
        IdentifierTree i = new IdentifierTree(p1, p2, n);
        i.setVariable(v);
        i.setLeftValue(true);
        return new WriteTree(p1, p2, i);
        }
        case UVOZOVKA: {
        accept(UVOZOVKA);
        String string = lexer.getIdentifier();
        accept(IDENT);
        accept(UVOZOVKA);
        Position p2 = lexer.getLastEndPosition();
        LiteralTree l = new LiteralTree(p1, p2, Type.STRING, string);
        return new WriteTree(p1, p2, l);
        
        
        }
        default:
        
        error("expected: IDENT or CISLO or CISLOREAL or STRING, found: " + token);
        throw new ParserException();
        }*/

        VyrazTree vt = vyraz(s);
        Position p2 = lexer.getLastEndPosition();
        return new WriteTree(p1, p2, vt);

    }

    //cast PrirazeniDoPole: IDENT arrayPoint PRIRAZENI vyraz;
    PrirazeniTree castPrirazeniDoPole(Position p1, Position p2, SymTab s, String n) {

        VariableTree v = s.find(n);
        if (v == null) {
            semanticError(p1, p2, "unknown identifier: " + n);
            throw new SemanticException();
        }

        if (s.find(n).getType() != Type.ARRAY) {
            semanticError(p1, p2, "Not ARRAY type " + n);
            throw new SemanticException();
        }

        VyrazTree arrayPoint = arrayPoint(s);
        p2 = lexer.getLastEndPosition();
        accept(PRIRAZENI);

        VyrazTree vyraz = vyraz(s);
        ArrayTree at = new ArrayTree(p1, p2, arrayPoint, n, aTab.numberOfDimension(n));
        at.setVariable(v);
        at.setLeftValue(true);
        Position p3 = lexer.getLastEndPosition();
        return new PrirazeniTree(p1, p3, at, vyraz);
    }

//castVyraz
//	:	IDENT PRIRAZENI vyraz ;
    //FIXME Deleni IDENTU
    PrirazeniTree castVyraz(SymTab s) {
        Position p1 = lexer.getBeginPosition();
        String n = lexer.getIdentifier();
        accept(IDENT);
        Position p2 = lexer.getLastEndPosition();
        VariableTree v = s.find(n);
        if (v == null) {
            semanticError(p1, p2, "unknown identifier: " + n);
            throw new SemanticException();
        }
        if (v.getType() == Type.ARRAY) {
            if (token == LHZ) {
                return castPrirazeniDoPole(p1, p2, s, n);
            }
        }


        IdentifierTree i = new IdentifierTree(p1, p2, n);
        i.setVariable(v);
        i.setLeftValue(true);
        accept(PRIRAZENI);
        VyrazTree vyraz = null;

        //if (v.getType() == Type.STRING) {

        //String string = lexer.getString();
        // vyraz = new LiteralTree(p1, p2, Type.STRING, string);
        // accept(STRING);

        //} else
        if (v.getType() == Type.ARRAY) {
            vyraz = castArray(p1, p2, n, s);
            VariableTree vt = v.clone();
            vt.setType(Type.POINTER);
            i.setVariable(vt);

        } else {
            vyraz = vyraz(s);
        }
        Position p3 = lexer.getLastEndPosition();
        //i.getVariable().setType(Type.POINTER);
        return new PrirazeniTree(p1, p3, i, vyraz);

    }

    //castArray: IDENT (array)
    //         | alloc  
    //         | dealloc 
    VyrazTree castArray(Position p1, Position p2, String name, SymTab s) {
        switch (token) {
            case IDENT: {
                String n = lexer.getIdentifier();
                accept(IDENT);
                VariableTree v = s.find(n);
                if (v == null) {
                    semanticError(p1, p2, "unknown identifier: " + n);
                    throw new SemanticException();
                }
                if (v.getType() == Type.ARRAY) {
                    if (aTab.contains(n)) {
                        aTab.insert(name, aTab.numberOfDimension(n));
                        IdentifierTree it = new IdentifierTree(p1, p2, n);
                        VariableTree vt = v.clone();
                        vt.setType(Type.POINTER);
                        it.setVariable(vt);
                        return it;

                    } else {
                        semanticError(p1, p2, "Not allocated : " + n);
                        throw new SemanticException();
                    }

                } else {
                    semanticError(p1, p2, "Invalid type: " + n);
                    throw new SemanticException();
                }
            }
            case LHZ: {
                dimensionCount = 1;
                VyrazTree alloc = arrayPoint(s);
                aTab.insert(name, dimensionCount);
                return new AllocTree(p1, p2, alloc, dimensionCount);

            }
            case PHZ: {
                aTab.delete(name);

                return dealloc();
            }
            default: {
                error("expected: IDENT or [ or ], found: " + token);
                throw new ParserException();
            }

        }
    }

    // arrayPoint: LHZ vyrazZbytek PHZ arrayPointZbytek
    VyrazTree arrayPoint(SymTab s) {
        Position p1 = lexer.getBeginPosition();
        accept(LHZ);
        VyrazTree vt = vyraz(s);
        accept(PHZ);
        Position p2 = lexer.getBeginPosition();

        VyrazTree vyraz = arrayPointZbytek(vt, s);
        return vyraz;

    }

    //dealloc: ][;
    DeallocTree dealloc() {
        Position p1 = lexer.getBeginPosition();
        accept(PHZ);
        Position p2 = lexer.getEndPosition();
        accept(LHZ);


        return new DeallocTree(p1, p2);

    }

    //arrayPointZbytek: LHZ vyraz PHZ arrayPointZbytek
    //            | ;
    VyrazTree arrayPointZbytek(VyrazTree dalsi, SymTab s) {
        if (token == LHZ) {
            dimensionCount++;
            Position p1 = lexer.getBeginPosition();
            accept(LHZ);

            VyrazTree velikost = vyraz(s);

            Position p2 = lexer.getEndPosition();
            accept(PHZ);

            DimensionTree dt = new DimensionTree(p1, p2, dalsi, velikost);
            return arrayPointZbytek(dt, s);
        }

        return dalsi;
    }

//volaniFunkce - muze slouzit i jako volani hodnot z pole
//	:	 LZ argument PZ
//  |   :        arrayPoint  
//  |    ;
    VyrazTree volaniFunkce(Position p1, String n, SymTab s) {
        if (token == LZ) {
            accept(LZ);
            Position p2 = lexer.getLastEndPosition();
            VariableFunTree v = funTab.find(n);
            if (v == null) {
                semanticError(p1, p2, "unknown function: " + n);
            }
            Argument2Tree arg = argument2(s);
            if (v.getArg().getPocet() != arg.getPocet()) {
                semanticError(p1, p2, "unknown function wrong number of arg: " + n);
            }
            IdentifierFunTree i = new IdentifierFunTree(p1, p2, n, arg);
            i.setVariable(v);

            accept(PZ);
            return i;
        }


        Position p2 = lexer.getLastEndPosition();
        VariableTree v = s.find(n);
        if (v == null) {
            semanticError(p1, p2, "unknown identifier: " + n);
        }
        if (v.getType() == Type.ARRAY) {
            VyrazTree arrayPoint = arrayPoint(s);
            ArrayTree at = new ArrayTree(p1, p2, arrayPoint, n, aTab.numberOfDimension(n));
            at.setRight(true);
            at.setVariable(v);
            return at;
        }
        IdentifierTree i = new IdentifierTree(p1, p2, n);
        i.setVariable(v);

        return i;
    }

    // argument2
//      :	CISLO argument2zbytek
//      |	CISLOREAL argument2zbytek
//      |	IDENT argument2zbytek;
    Argument2Tree argument2(SymTab s) {
        List<VyrazTree> vt = new ArrayList<VyrazTree>();
        VyrazTree v = vyraz(s);
        /*
        switch (token) {
        case CISLO: {
        
        Object v = lexer.getValue();
        accept(CISLO);
        Position p2 = lexer.getLastEndPosition();
        LiteralTree l = new LiteralTree(p1, p2, Type.INTEGER, v);
        vt.add(l);
        return argument2zbytek(vt, 1);
        
        }
        case CISLOREAL: {
        
        Object v = lexer.getValue();
        accept(CISLOREAL);
        Position p2 = lexer.getLastEndPosition();
        LiteralTree l = new LiteralTree(p1, p2, Type.REAL, v);
        vt.add(l);
        return argument2zbytek(vt, 1);
        
        }
        case IDENT: {
        
        String n = lexer.getIdentifier();
        accept(IDENT);
        Position p2 = lexer.getLastEndPosition();
        VariableTree v = symTab.find(n);
        if (v == null) {
        semanticError(p1, p2, "unknown identifier: " + n);
        }
        IdentifierTree i = new IdentifierTree(p1, p2, n);
        i.setVariable(v);
        vt.add(i);
        return argument2zbytek(vt, 1);
        
        
        
        }
        }*/
        vt.add(v);
        return argument2zbytek(vt, 1, s);


    }
//argument2zbytek
//	:	CARKA CISLO argument2zbytek
//	|	CARKA CISLOREAL argument2zbytek
//	|	CARKA IDENT argument2zbytek
//	|	;

    Argument2Tree argument2zbytek(List<VyrazTree> vt, int pocet, SymTab s) {

        Position p1 = lexer.getBeginPosition();
        if (token == CARKA) {
            accept(CARKA);
            VyrazTree v = vyraz(s);
            vt.add(v);
            pocet++;
            return argument2zbytek(vt, pocet, s);
            /*
            switch (token) {
            case CISLO: {
            pocet++;
            Object v = lexer.getValue();
            accept(CISLO);
            Position p2 = lexer.getLastEndPosition();
            LiteralTree l = new LiteralTree(p1, p2, Type.INTEGER, v);
            vt.add(l);
            return argument2zbytek(vt, pocet);
            
            }
            case CISLOREAL: {
            pocet++;
            Object v = lexer.getValue();
            accept(CISLOREAL);
            Position p2 = lexer.getLastEndPosition();
            LiteralTree l = new LiteralTree(p1, p2, Type.REAL, v);
            vt.add(l);
            return argument2zbytek(vt, pocet);
            }
            case IDENT: {
            pocet++;
            String n = lexer.getIdentifier();
            accept(IDENT);
            Position p2 = lexer.getLastEndPosition();
            VariableTree v = symTab.find(n);
            if (v == null) {
            semanticError(p1, p2, "unknown identifier: " + n);
            }
            IdentifierTree i = new IdentifierTree(p1, p2, n);
            i.setVariable(v);
            vt.add(i);
            return argument2zbytek(vt, pocet);
            
            }
            }*/
        }
        Position p2 = lexer.getLastEndPosition();

        return new Argument2Tree(p1, p2, vt, pocet);

    }

//vyraz 	:	clen vyrazZbytek;
    VyrazTree vyraz(SymTab s) {
        Position p1 = lexer.getBeginPosition();
        VyrazTree c = clen(s);
        return vyrazZbytek(p1, c, s);
    }

//vyrazZbytek
//	:	PLUS clen vyrazZbytek
//	| 	MINUS clen vyrazZbytek
//	|	;
    VyrazTree vyrazZbytek(Position p1, VyrazTree c1, SymTab s) {
        switch (token) {
            case PLUS: {
                accept(PLUS);
                Operator o = Operator.PLUS;
                VyrazTree c2 = clen(s);
                Position p2 = lexer.getBeginPosition();
                BinaryTree tree = new BinaryTree(p1, p2, o, c1, c2);
                return vyrazZbytek(p1, tree, s);
            }
            case MINUS: {
                accept(MINUS);
                Operator o = Operator.MINUS;
                VyrazTree c2 = clen(s);
                Position p2 = lexer.getBeginPosition();
                BinaryTree tree = new BinaryTree(p1, p2, o, c1, c2);
                return vyrazZbytek(p1, tree, s);
            }

        }
        return c1;
    }

//clen 	:	faktor clenZbytek;
    VyrazTree clen(SymTab s) {
        Position p1 = lexer.getBeginPosition();
        VyrazTree f = faktor(s);
        return clenZbytek(p1, f, s);


    }

//clenZbytek
//	:	KRAT faktor clenZbytek
//	|	DELENO faktor clenZbytek
//	|	;
    VyrazTree clenZbytek(Position p1, VyrazTree f1, SymTab s) {
        switch (token) {
            case KRAT: {
                accept(KRAT);
                Operator o = Operator.KRAT;
                VyrazTree f2 = faktor(s);
                Position p2 = lexer.getBeginPosition();
                BinaryTree tree = new BinaryTree(p1, p2, o, f1, f2);
                return clenZbytek(p1, tree, s);
            }

            case DELENO: {
                accept(DELENO);
                Operator o = Operator.DELENO;
                VyrazTree f2 = faktor(s);
                Position p2 = lexer.getBeginPosition();
                BinaryTree tree = new BinaryTree(p1, p2, o, f1, f2);
                return clenZbytek(p1, tree, s);
            }

        }
        return f1;
    }

//faktor	:	LZ vyraz PZ
//	|	IDENT volaniFunkce;
//	|	CISLO
    // |   CISLOREAL
    //STRING;
    VyrazTree faktor(SymTab s) {
        switch (token) {
            case LZ: {
                accept(LZ);
                VyrazTree v = vyraz(s);
                accept(PZ);
                return v;
            }
            case IDENT: {
                Position p1 = lexer.getBeginPosition();
                String n = lexer.getIdentifier();
                accept(IDENT);
                return volaniFunkce(p1, n, s);

            }
            case CISLO: {
                Position p1 = lexer.getBeginPosition();
                Object v = lexer.getValue();
                accept(CISLO);
                Position p2 = lexer.getLastEndPosition();
                return new LiteralTree(p1, p2, Type.INTEGER, v);


            }
            case STRING: {
                Position p1 = lexer.getBeginPosition();
                Object v = lexer.getString();
                accept(STRING);
                Position p2 = lexer.getLastEndPosition();
                return new LiteralTree(p1, p2, Type.STRING, v);


            }

            case CISLOREAL: {
                Position p1 = lexer.getBeginPosition();
                Object v = lexer.getValue();
                accept(CISLOREAL);
                Position p2 = lexer.getLastEndPosition();
                return new LiteralTree(p1, p2, Type.REAL, v);
            }

            case READ: {
                Position p1 = lexer.getBeginPosition();
                accept(READ);
                accept(LZ);

                VyrazTree nameFile = vyraz(s);

                accept(PZ);

                Position p2 = lexer.getLastEndPosition();
                return new ReadFileTree(p1, p2, nameFile);
            }

            default:
                error("expected: IDENT or CISLO or (, found: " + token);
                throw new ParserException();

        }
    }

//castPorovnani
//	:	vyraz porovnani vyraz;
    BinaryTree castPorovnani(SymTab s) {
        Position p1 = lexer.getBeginPosition();
        VyrazTree v1 = vyraz(s);
        Operator o = porovnani();
        VyrazTree v2 = vyraz(s);
        Position p2 = lexer.getEndPosition();
        return new BinaryTree(p1, p2, o, v1, v2);
    }

//porovnani
//	:	VETSI
//	| 	MENSI
//	|	ROVNO
//	|	VETSIROV
//	|	MENSIROV
//	|       NEROVNO;
    Operator porovnani() {
        switch (token) {
            case VETSI:
                accept(VETSI);
                return Operator.VETSI;
            case NEROVNO:
                accept(NEROVNO);
                return Operator.NEROVNO;

            case MENSI:
                accept(MENSI);
                return Operator.MENSI;
            case ROVNO:
                accept(ROVNO);
                return Operator.ROVNO;
            case VETSIROV:
                accept(VETSIROV);
                return Operator.VETSIROV;
            case MENSIROV:
                accept(MENSIROV);
                return Operator.MENSIROV;
            default:
                error("expected: > or < or == or >= or <= or !=, found: " + token);
                throw new ParserException();
        }
    }

//castIf	:	IF LZ castPorovnani PZ  LSZ cast PSZ castElse; ;
    CastIfTree castIf(SymTab s) {
        Position p1 = lexer.getBeginPosition();
        accept(IF);
        accept(LZ);
        BinaryTree p = castPorovnani(s);
        accept(PZ);
        accept(LSZ);
        List<VlastniProgramTree> i = vlastniProgram(new ArrayList<VlastniProgramTree>(), s);
        accept(PSZ);
        List<VlastniProgramTree> e = castElse(s);
        Position p2 = lexer.getLastEndPosition();
        return new CastIfTree(p1, p2, p, i, e);

    }

//castElse:	LSZ vlastniProgram PSZ
//        |  ;
    List<VlastniProgramTree> castElse(SymTab s) {
        if (token == ELSE) {
            accept(ELSE);
            accept(LSZ);
            List<VlastniProgramTree> e = vlastniProgram(new ArrayList<VlastniProgramTree>(), s);
            accept(PSZ);
            return e;
        }
        return null;
    }

//castFor	:   FOR LZ  castVyraz STREDNIK castPorovnani STREDNIK  castVyraz PZ zbytekFor;
    CastForTree castFor(SymTab s) {
        Position p1 = lexer.getBeginPosition();
        accept(FOR);
        accept(LZ);

        PrirazeniTree v1 = castVyraz(s);
        accept(STREDNIK);
        BinaryTree p = castPorovnani(s);
        accept(STREDNIK);

        PrirazeniTree v2 = castVyraz(s);
        accept(PZ);
        List<VlastniProgramTree> prog = zbytekFor(s);
        Position p2 = lexer.getLastEndPosition();
        return new CastForTree(p1, p2, v1, p, v2, prog);
    }
//zbytekFor
//	:	LSZ cast PSZ;

    List<VlastniProgramTree> zbytekFor(SymTab s) {
        accept(LSZ);
        List<VlastniProgramTree> prog = vlastniProgram(new LinkedList<VlastniProgramTree>(), s);
        accept(PSZ);
        return prog;
    }
}
